# encoding: utf-8
from abc import abstractmethod


class Observer():
    """
    Implements Observer entity from Observer pattern.
    An Observer can subscribe to an Observable to receive all future notifications.
    """

    def __init__(self, name="observer"):
        """
        Creates Observer.
        An Observer is uniquely defined by its name.vDefault name is 'observer'.
        """
        if not isinstance(name, str):
            raise TypeError("Expected string for name")

        self.name = name

    @abstractmethod
    def update(self, data):
        """
        Automatically called each time Observer receives a notification.
        A notification updates data value
        """
        pass


def __str__(self):
    """
    Simply returns Observer name
    """
    return self.name


class Observable():
    """
    Implements Observable entity from Observer pattern.
    The Observable owns information of interest for one or more other entities.
    Observers subscribe to Observable to receive further notifications.
    Each time data is set, all Observer are notified.
    """

    def __init__(self):
        """
        Creates Observable.
        No subscriber is registered yet.
        """
        self.data = None
        self.observers = []

    def subscribe(self, observer):
        """
        Adds observer to notification list.
        """
        if not isinstance(observer, Observer):
            raise TypeError("Subscriber must be Observer")

        if (observer in self.observers):
            raise ValueError("Observer already subscribed")
        self.observers.append(observer)

    def unsubscribe(self, observer):
        """
        Removes observer from notification list.
        """
        if not isinstance(observer, Observer):
            raise TypeError("Unsubscriber must be Observer")

        if not (observer in self.observers):
            raise ValueError("Observer not subscribed")
        self.observers.remove(observer)

    def notify(self, data):
        """
        Sends notification data to all subscribed observer
        """
        for observer in self.observers:
            observer.update(data)
